package com.yuyou.fn.tms.filter;



import com.github.wens.mybatisplus.toolkit.IdWorker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class LogFilter implements Filter {

    private static class LogInputStream extends ServletInputStream {

        private ServletInputStream inputStream ;

        private ByteArrayOutputStream readBytes;

        LogInputStream(ServletInputStream inputStream){
            this.inputStream = inputStream ;
            this.readBytes = new ByteArrayOutputStream();
        }


        @Override
        public int read() throws IOException {
            int r  = inputStream.read();
            if(r != -1 ){
                readBytes.write(r);
            }
            return r ;
        }

        @Override
        public int read(byte[] b, int off, int len) throws IOException {
            int r = inputStream.read(b, off, len);
            if(r != -1 ){
                readBytes.write(b,off,r);
            }
            return r ;
        }

        @Override
        public int read(byte[] b) throws IOException {
            int r = inputStream.read(b);
            if(r != -1 ){
                readBytes.write(b,0,r);
            }
            return r ;
        }

        @Override
        public int readLine(byte[] b, int off, int len) throws IOException {
            int r = inputStream.readLine(b, off, len);
            if(r != -1 ){
                readBytes.write(b,off,r);
            }
            return r ;
        }

        public byte[] gotReadBytes(){
            return readBytes.toByteArray();
        }

        @Override
        public void close() throws IOException {
            inputStream.close();
        }

        @Override
        public long skip(long n) throws IOException {
            return inputStream.skip(n);
        }

        @Override
        public int available() throws IOException {
            return inputStream.available();
        }

        @Override
        public synchronized void mark(int readlimit) {
            inputStream.mark(readlimit);
        }

        @Override
        public synchronized void reset() throws IOException {
            inputStream.reset();
        }

        @Override
        public boolean markSupported() {
            return inputStream.markSupported();
        }
    }

    private static class LogOutputStream extends ServletOutputStream {

        private ServletOutputStream outputStream ;

        private ByteArrayOutputStream writeBytes ;

        LogOutputStream(ServletOutputStream outputStream){
            this.outputStream = outputStream ;
            this.writeBytes = new ByteArrayOutputStream();
        }

        @Override
        public void write(int b) throws IOException {
            outputStream.write(b);
            writeBytes.write(b);

        }

        @Override
        public void write(byte[] b) throws IOException {
            outputStream.write(b);
            writeBytes.write(b);
        }

        @Override
        public void write(byte[] b, int off, int len) throws IOException {
            outputStream.write(b, off, len);
            writeBytes.write(b,off,len);
        }

        public byte[] gotWiteBytes(){
            return writeBytes.toByteArray();
        }

        @Override
        public void print(String s) throws IOException {
            outputStream.print(s);
        }

        @Override
        public void print(boolean b) throws IOException {
            outputStream.print(b);
        }

        @Override
        public void print(char c) throws IOException {
            outputStream.print(c);
        }

        @Override
        public void print(int i) throws IOException {
            outputStream.print(i);
        }

        @Override
        public void print(long l) throws IOException {
            outputStream.print(l);
        }

        @Override
        public void print(float f) throws IOException {
            outputStream.print(f);
        }

        @Override
        public void print(double d) throws IOException {
            outputStream.print(d);
        }

        @Override
        public void println() throws IOException {
            outputStream.println();
        }

        @Override
        public void println(String s) throws IOException {
            outputStream.println(s);
        }

        @Override
        public void println(boolean b) throws IOException {
            outputStream.println(b);
        }

        @Override
        public void println(char c) throws IOException {
            outputStream.println(c);
        }

        @Override
        public void println(int i) throws IOException {
            outputStream.println(i);
        }

        @Override
        public void println(long l) throws IOException {
            outputStream.println(l);
        }

        @Override
        public void println(float f) throws IOException {
            outputStream.println(f);
        }

        @Override
        public void println(double d) throws IOException {
            outputStream.println(d);
        }

        @Override
        public void flush() throws IOException {
            outputStream.flush();
        }

        @Override
        public void close() throws IOException {
            outputStream.close();
        }
    }


    private final  Logger log = LoggerFactory.getLogger(this.getClass());


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }


    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        Long start=System.currentTimeMillis();
        LogInputStream logInputStream = new LogInputStream(request.getInputStream());
        LogOutputStream logOutputStream = new LogOutputStream(response.getOutputStream());
        chain.doFilter(new HttpServletRequestWrapper((HttpServletRequest) request){
            @Override
            public ServletInputStream getInputStream() throws IOException {
                return logInputStream;
            }
        },new HttpServletResponseWrapper((HttpServletResponse) response){
            @Override
            public ServletOutputStream getOutputStream() throws IOException {
                return logOutputStream;
            }
        });
        String requestId = String.valueOf(IdWorker.getId()) ;
        ((HttpServletResponse) response).setHeader("Request-Id" ,requestId);
        log.info("useTime={},requestId={},uri={},request={},response={}",System.currentTimeMillis()-start,requestId,((HttpServletRequest) request).getRequestURI(),new String(logInputStream.gotReadBytes(),"utf-8"),new String(logOutputStream.gotWiteBytes(),"utf-8"));
    }

    @Override
    public void destroy() {

    }
}
